package com.linkdood.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linkdood.app.constants.AuthoritiesConstants;
import com.linkdood.app.constants.ErrorCode;
import com.linkdood.app.domain.*;
import com.linkdood.app.dto.*;
import com.linkdood.app.exception.GeneralException;
import com.linkdood.app.exception.InvalidParamException;
import com.linkdood.app.exception.UserExistException;
import com.linkdood.app.exception.UserNotFoundException;
import com.linkdood.app.mapper.AccountMapper;
import com.linkdood.app.security.UserModelDetailsServiceImpl;
import com.linkdood.app.service.*;
import com.linkdood.app.utils.MD5Util;
import com.linkdood.app.utils.PinYinUtil;
import com.linkdood.app.utils.ResourceUtil;
import com.linkdood.app.vm.AccountVM;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 系统用户
 *
 * @author YunJ
 */
@Slf4j
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    private final SysMenuService sysMenuService;

    public static final String USER_DISPLAY_NAME = "普通用户";

    private final UserService userService;

    private final UserDataService userDataService;

    private final OrganizationService organizationService;

    private final AuthorityService authorityService;

    private final CustomRoleService customRoleService;

    private final VerifyCodeService verifyCodeService;

    public AccountServiceImpl(SysMenuService sysMenuService,
                              UserService userService,
                              UserDataService userDataService,
                              OrganizationService organizationService,
                              AuthorityService authorityService,
                              CustomRoleService customRoleService,
                              VerifyCodeService verifyCodeService) {
        this.sysMenuService = sysMenuService;
        this.userService = userService;
        this.userDataService = userDataService;
        this.organizationService = organizationService;
        this.authorityService = authorityService;
        this.customRoleService = customRoleService;
        this.verifyCodeService = verifyCodeService;
    }


    /**
     * 获取当前用户的 accountId
     * Authentication.getName 为 username, 是在
     *
     * @see UserModelDetailsServiceImpl 中指定的
     */
    @Override
    public String getCurrentAccountId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            throw new InvalidParamException();
        }
        return authentication.getName();
    }

    /**
     * 获取当前登录用户账号
     */
    @Override
    public Account getCurrentAccount() {
        return getById(getCurrentAccountId());
    }

    /**
     * 获取当前登录用户的信息
     */
    @Override
    public AccountVM getCurrentAccountInfo() {
        Account account = getById(getCurrentAccountId());
        if (account == null) {
            throw new GeneralException(ErrorCode.ERROR_USER_NOT_FOUND);
        }
        return accountToAccountVm(account);
    }
    @Override
    public Optional<Account> getAccount(String userId) {
        log.debug("getAccount() : userId={}", userId);
        List<Account> accounts = lambdaQuery().eq(Account::getUserId, userId).list();
        if (CollectionUtils.isNotEmpty(accounts)) {
            return Optional.of(accounts.get(0));
        }
        return Optional.empty();
    }

    // FIXME 多表联合分页查询，暂时使用 Mybatis 旧版 XML 格式，以后优化
    @Override
    public Page<AccountVM> pageAccounts(String displayName, String role, String username, Page<Account> page) {
        log.debug("pageAccounts() : displayName={}, role={}", displayName, role);
        Page<AccountVM> accountPage = new Page<>();
        // 分别单独查询总数和分页内容
        Long accountTotal = getBaseMapper().countUserAccountPageByNameAndRole(displayName, role, username);
        if (accountTotal == 0L) {
            accountPage.setTotal(0L);
            return accountPage;
        }
        Long pageSkip = (page.getCurrent() - 1) * page.getSize();
        List<Account> accountList = getBaseMapper().listUserAccountPageByNameAndRole(displayName, role, username, pageSkip, page.getSize());
        List<AccountVM> accountVmList = accountList.stream().map(this::accountToAccountVm).collect(Collectors.toList());

        // 拼接
        accountPage.setTotal(accountTotal);
        accountPage.setSize(page.getSize());
        accountPage.setRecords(accountVmList);

        return accountPage;
    }

    /**
     * 创建用户并绑定
     * @param accountDTO 入参DTO
     * @return AccountVM
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AccountVM saveAccount(AccountDTO accountDTO) {
        if (StringUtils.isEmpty(accountDTO.getRole()) || StringUtils.isEmpty(accountDTO.getUsername())) {
            throw new InvalidParamException();
        }
        Account account = new Account();
        account.setUsername(accountDTO.getUsername());
        account.setRole(accountDTO.getRole());

        // 绑定用户
        if (StringUtils.isNotEmpty(accountDTO.getUserId())) {
            account.setUserId(accountDTO.getUserId());
        }
        Account result = saveAccount(account, accountDTO.getCustomRoleId());
        return accountToAccountVm(result);
    }

    /**
     * 创建用户，支持设置角色和绑定用户
     * @param account 用户信息
     * @param customRoleId 角色
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Account saveAccount(Account account, String customRoleId) {
        List<Account> accounts = this.lambdaQuery().eq(Account::getUsername, account.getUsername()).list();
        if (CollectionUtils.isNotEmpty(accounts)) {
            throw new UserExistException();
        }

        // role 是否为以下管理员和用户，超级用户不允许创建
        if (!AuthoritiesConstants.USER.equals(account.getRole())
                && !AuthoritiesConstants.MANAGER.equals(account.getRole())) {
            throw new InvalidParamException("userRole");
        }
        account.setPassword(ResourceUtil.encodePwd(MD5Util.MD5Encode(AuthoritiesConstants.DEFAULT_PASSWORD)));
        this.save(account);

        // 同时保存用户权限; 只有管理员才能自定义角色
        if (AuthoritiesConstants.MANAGER.equals(account.getRole())) {

            // 管理员必须绑定角色，否则抛出异常
            if (StringUtils.isBlank(customRoleId)) {
                throw new GeneralException(ErrorCode.ERROR_MANAGER_MUST_HAVE_ROLE);
            }

            Authority authority = new Authority();
            authority.setAccountId(account.getId());
            authority.setCustomRoleId(customRoleId);
            authorityService.save(authority);
        }
        return account;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void resetPassword(String id) {
        Optional<Account> userAccountOptional = lambdaQuery().eq(Account::getId, id).oneOpt();
        if (!userAccountOptional.isPresent()) {
            throw new UserNotFoundException();
        }
        Account account = userAccountOptional.get();
        account.setPassword(ResourceUtil.encodePwd(MD5Util.MD5Encode(AuthoritiesConstants.DEFAULT_PASSWORD)));
        this.updateById(account);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public AccountVM update(String accountId, AccountDTO accountDTO) {
        Account account = this.getById(accountId);
        if (account == null) {
            throw new GeneralException(ErrorCode.ERROR_USER_NOT_FOUND);
        }
        // 不能将不是超管的用户改为超管
        if (!AuthoritiesConstants.SUPER_ADMIN.equals(account.getRole()) && AuthoritiesConstants.SUPER_ADMIN.equals(accountDTO.getRole())) {
            throw new GeneralException(ErrorCode.ERROR_ACCOUNT_PERMISSION);
        }
        // todo 会批量编辑
        // FIXME(gaoliusheng) 确认无用代码需要删除而不是注释
//        LambdaUpdateWrapper<Account> updateWrapper = new LambdaUpdateWrapper<>();
//        updateWrapper.set(Account::getUsername, accountDTO.getUsername());
//        updateWrapper.set(Account::getRole, accountDTO.getRole());
//        if (StringUtils.isNotBlank(accountDTO.getUserId())) {
//            updateWrapper.set(Account::getUserId, accountDTO.getUserId());
//        } else {
//            updateWrapper.set(Account::getUserId, null);
//        }
//        getBaseMapper().update(account, updateWrapper);
        account.setUsername(accountDTO.getUsername());
        if (StringUtils.isNotBlank(accountDTO.getUserId())) {
            account.setUserId(accountDTO.getUserId());
        }
        account.setRole(accountDTO.getRole());
        this.updateById(account);


        User user = new User();
        user.setId(account.getUserId());
        user.setDisplayName(accountDTO.getDisplayName());
        userService.updateById(user);

        Optional<UserData> userDataOptional = userDataService.getByUserId(account.getUserId());
        if (userDataOptional.isPresent()) {
            UserData userData = userDataOptional.get();
            userData.setBirthday(accountDTO.getBirthday());
            userData.setEmails(accountDTO.getEmails());
            userData.setSex(accountDTO.getSex());
            userData.setPortraitUrl(accountDTO.getPortraitUrl());
            userDataService.updateById(userData);
        }

        // 如果权限输入为空，直接返回
        if (StringUtils.isBlank(accountDTO.getCustomRoleId())) {
            return accountToAccountVm(account);
        }

        // 更新菜单权限，如果存在则修改，否则，新增
        Optional<Authority> authorityOpt = authorityService.getByAccountId(accountId);
        if (authorityOpt.isPresent()) {
            Authority authority = authorityOpt.get();
            authority.setCustomRoleId(accountDTO.getCustomRoleId());
            authorityService.updateById(authority);
        } else {
            Authority newAuthority = new Authority();
            newAuthority.setCustomRoleId(accountDTO.getCustomRoleId());
            newAuthority.setAccountId(accountId);
            authorityService.save(newAuthority);
        }

        return accountToAccountVm(account);
    }

    /**
     * 更新用户密码
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AccountVM updateAccountPassword(Account account, PasswordDTO params) {
        String oldPassword = ResourceUtil.base64Decode(params.getOldPassword());
        String newPassword = ResourceUtil.base64Decode(params.getNewPassword());
        // 新老密码相同
        if (oldPassword.equals(newPassword)) {
            throw new GeneralException(ErrorCode.ERROR_USER_PASSWORD);
        }

        Account oldUser = this.getById(account.getId());
        if (!ResourceUtil.matchPwd(oldPassword, oldUser.getPassword())) {
            throw new GeneralException(ErrorCode.ERROR_USER_PASSWORD);
        }
        account.setPassword(ResourceUtil.encodePwd(MD5Util.MD5Encode(newPassword)));
        this.updateById(account);
        return accountToAccountVm(account);
    }

    /**
     * 验证用户名是否存在
     * 如果存在，随机生成一个；
     * 如果不存在，将输入的用户名返回
     */
    @Override
    public String checkUsername(String username) {
        Optional<Account> account = lambdaQuery().eq(Account::getUsername, username).oneOpt();
        if (!account.isPresent()) {
            return username;
        }
        String randomUsername = username + "_" + ResourceUtil.generatingRandomAlphanumericString(4);
        return checkUsername(randomUsername);
    }

    /**
     * account 转 account DTO
     *
     * @param account Account
     * @return AccountVM
     */
    private AccountVM accountToAccountVm(Account account) {
        AccountVM accountVM = new AccountVM();
        accountVM.setId(account.getId());
        accountVM.setUsername(account.getUsername());
        accountVM.setRole(account.getRole());
        accountVM.setCreateTime(account.getCreateTime());
        accountVM.setUpdateTime(account.getUpdateTime());

        // FIXME(gaoliusheng) 确认无用代码需要删除而不是注释
//        // 设置用户角色和菜单权限
//        Optional<Authority> roles = authorityService.findByAccountId(account.getId());
//        if (roles.isPresent()) {
//            CustomRole role = customRoleService.getById(roles.get().getCustomRoleId());
//            accountVM.setCustomRole(role);
//        }
        CustomRole role;
        // 设置用户角色和菜单权限
        Optional<Authority> roles = authorityService.getByAccountId(account.getId());
        if (roles.isPresent()) {
            role = customRoleService.getById(roles.get().getCustomRoleId());
            if (this.getCurrentAccountId().equals(account.getId())) {
                Set<String> menuPermission = this.getMenuPermission(account.getId());
                role.setPermissions(menuPermission);
            }
        } else {
            role = new CustomRole();
            Set<String> menuPermission = this.getMenuPermission(account.getId());
            role.setPermissions(menuPermission);
        }
        accountVM.setCustomRole(role);

        // 如果账号未绑定用户，则直接返回
        if (StringUtils.isEmpty(account.getUserId())) {
            return accountVM;
        }

        // 如果查询不到用户，则直接返回
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getId, account.getUserId()).eq(User::getDeleted, false);
        User user = userService.getOne(queryWrapper);
        if (user == null) {
            return accountVM;
        }

        // 构造用户信息
        AccountVM.InnerUser innerUser = new AccountVM.InnerUser();
        innerUser.setId(user.getId());
        innerUser.setDisplayName(user.getDisplayName());
        innerUser.setSourceId(user.getSourceId());
        innerUser.setThirdPartyId(user.getThirdPartyId());

        Optional<UserData> userDataOptional = userDataService.getByUserId(user.getId());
        if (userDataOptional.isPresent()) {
            UserData userData = userDataOptional.get();
            innerUser.setBirthday(userData.getBirthday());
            innerUser.setEmails(userData.getEmails());
            innerUser.setSex(userData.getSex());
            innerUser.setPortraitUrl(userData.getPortraitUrl());
        }

        // 构造组织架构列表
        List<Organization> organizations = organizationService.listByUserId(account.getUserId());
        List<AccountVM.InnerOrg> innerOrgs = organizations.stream().map(AccountVM.InnerOrg::new).collect(Collectors.toList());
        innerUser.setOrg(innerOrgs);

        // 设置 user 信息
        accountVM.setUser(innerUser);

        return accountVM;
    }

    /**
     * 检查用户名是否存在
     * @param username 用户名(手机号)
     * @return true/false
     */
    @Override
    public boolean accountIsExist(String username) {
        Optional<Account> account = this.lambdaQuery().eq(Account::getUsername, username).oneOpt();
        return account.isPresent();
    }

    /**
     * 用户注册
     * @param req 注册入参
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterDTO req) {
        verifyCodeService.checkVerifyCode(req.getBid(), req.getPhone(), req.getCode());

        if (accountIsExist(req.getPhone())) {
            throw new UserExistException();
        }

        if (!StringUtils.equals(req.getPassword(), req.getConfirmPassword())) {
            throw new GeneralException(ErrorCode.ERROR_PASSWORD_NOT_SAME);
        }
        User user = new User();
        user.setDisplayName(USER_DISPLAY_NAME + ResourceUtil.generatingRandomAlphanumericString(5));
        userService.save(user);

        UserData userData = new UserData();
        userData.setUserId(user.getId());
        userData.setPhoneNums(req.getPhone());
        userData.setRealName(user.getDisplayName());
        userData.setName(user.getDisplayName());
        userData.setFirstPinYin(PinYinUtil.getFirstPinyin(user.getDisplayName()));
        userDataService.save(userData);

        Account account = new Account();
        account.setUserId(user.getId());
        account.setUsername(req.getPhone());
        String password = ResourceUtil.base64Decode(req.getPassword());
        account.setPassword(ResourceUtil.encodePwd(MD5Util.MD5Encode(password)));
        account.setRole(AuthoritiesConstants.USER);
        this.save(account);
    }

    /**
     * 设置新密码(忘记密码)
     * @param req 新密码入参
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void newPassword(ResetPasswordDTO req) {
        Optional<Account> accountOptional = lambdaQuery().eq(Account::getUsername, req.getPhone()).oneOpt();
        if (accountOptional.isPresent()) {
            throw new UserNotFoundException();
        }

        if (!StringUtils.equals(req.getPassword(), req.getConfirmPassword())) {
            throw new GeneralException(ErrorCode.ERROR_PASSWORD_NOT_SAME);
        }

        verifyCodeService.checkVerifyCode(req.getBid(), req.getPhone(), req.getCode());

        Account account = accountOptional.get();
        account.setPassword(ResourceUtil.encodePwd(MD5Util.MD5Encode(req.getPassword())));
        this.updateById(account);
    }

    /**
     * 更新用户名
     * @param accountId 账户id
     * @param req 验证码参数
     */
    @Override
    public void updateUserName(String accountId, PhoneCodeDTO req) {
        Optional<Account> accountOptional = lambdaQuery().eq(Account::getId, accountId).oneOpt();
        if (accountOptional.isPresent()) {
            throw new UserNotFoundException();
        }

        if (accountIsExist(req.getPhone())) {
            throw new UserExistException();
        }

        verifyCodeService.checkVerifyCode(req.getBid(), req.getPhone(), req.getCode());

        Account account = accountOptional.get();
        account.setUsername(req.getPhone());
        this.updateById(account);
    }

    /**
     * 获取菜单数据权限
     *
     * @param id 用户信息
     * @return 菜单权限信息
     */
    @Override
    public Set<String> getMenuPermission(String id) {
        Set<String> perms = new HashSet<>();
        // 管理员拥有所有权限
        if (Account.isAdmin(id)) {
            perms.add("*:*:*");
        } else {
            perms.addAll(sysMenuService.listMenuPermsByUserId(id));
        }
        return perms;
    }

    @Override
    public List<Account> listAccountByUserName(String userName) {
        return lambdaQuery().eq(Account::getUsername, userName).list();
    }

    @Override
    public Optional<Account> getAccountByUserName(String userName) {
        return lambdaQuery().eq(Account::getUsername, userName).oneOpt();
    }
}
